#include <QTimer>
#include <QPainter>
#include <QMouseEvent>
#include "grid.h"

Grid::Grid(QWidget *parent) :
    QWidget(parent),mytimer(new QTimer(this)),grid_size(500)
{
    mytimer->setInterval(1);
    for(int i=0;i<grid_size;++i)for(int j=0;j<grid_size;++j)color[i][j]=0;
    origin_color="#000";
    key_type=0;
    rng.seed((unsigned long long)new char);
    connect(mytimer, SIGNAL(timeout()), this, SLOT(next_step()));
}

Grid::~Grid()
{

}

vector<pair<int,int>>Grid::generate_random_pointset(int size)
{
    vector<pair<int,int>>res;
    for(int i=0;i<size;++i)
    {
        int x=randint(1,grid_size),y=randint(1,grid_size);
        res.push_back(make_pair(x,y));
    }
    return res;
}

void Grid::clear()
{
    memset(color,0,sizeof(color));
    update();
}

void Grid::random_init()
{
    clear();
    const int limit=20;
    auto pointset=generate_random_pointset(randint(1,limit));
    for(auto&p:pointset)
    {
        int x=p.first,y=p.second;
        draw_star(x,y);
    }
    pointset=generate_random_pointset(randint(1,limit));
    for(auto&p:pointset)
    {
        int x=p.first,y=p.second;
        draw_ship(x,y);
    }
    pointset=generate_random_pointset(randint(1,limit));
    for(auto&p:pointset)
    {
        int x=p.first,y=p.second;
        draw_plane(x,y);
    }
    pointset=generate_random_pointset(randint(1,limit));
    for(auto&p:pointset)
    {
        int x=p.first,y=p.second;
        draw_rect(x,y,randint(1,10),randint(1,10));
    }
    update();
}

void Grid::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    paintGrid(p);
    paintUniverse(p);
}

void Grid::run_game()
{
    mytimer->start();
}

void Grid::set_normal()
{
    key_type=0;
}

void Grid::set_rect()
{
    key_type=1;
}

void Grid::set_plane()
{
    key_type=2;
}

void Grid::set_star()
{
    key_type=3;
}

void Grid::set_ship()
{
    key_type=4;
}

bool Grid::valid(int x,int y)
{
    return x>=0&&y>=0&&x<maxn&&y<maxn;
}

int Grid::randint(int l, int r)
{
    unsigned long long cur=rng();
    return cur%(r-l+1)+l;
}

void Grid::draw_rect(int i, int j, int w, int h)
{
    for(int a=0;a<w;++a)for(int b=0;b<h;++b)
    {
        int cx=i+a,cy=j+b;
        if(valid(cx,cy))
        {
            color[cx][cy]=1;
        }
    }
}

pair<int,int> Grid::turn(pair<int,int>o)
{
    pair<int,int>res;
    res.first=o.second;
    res.second=-o.first;
    return res;
}

void Grid::draw_plane(int k, int j)
{
    int id=randint(0,3);
    for(int i=0;i<5;++i)
    {
        int ox=plane[i][0],oy=plane[i][1];
        pair<int,int>cur=make_pair(ox,oy);
        for(int x=0;x<id;++x)
        {
            cur=turn(cur);
        }
        ox=cur.first,oy=cur.second;
        int cx=k+ox,cy=j+oy;
        if(valid(cx,cy))color[cx][cy]=1;
    }
}

void Grid::draw_ship(int k, int j)
{
    int id=randint(0,3);
    for(int i=0;i<10;++i)
    {
        int ox=ship[i][0],oy=ship[i][1];
        pair<int,int>cur=make_pair(ox,oy);
        for(int x=0;x<id;++x)
        {
            cur=turn(cur);
        }
        ox=cur.first,oy=cur.second;
        int cx=k+ox,cy=j+oy;
        if(valid(cx,cy))color[cx][cy]=1;
    }
}

void Grid::draw_star(int x, int y)
{
    int dx,dy,cx,cy;
    for(int i=-1;i<=1;++i)for(int j=-1;j<=1;++j)
    {
        if(!i||!j)continue;
        for(int a=2;a<=4;++a)
        {
            dx=i*1,dy=j*a;
            cx=dx+x,cy=dy+y;
            if(valid(cx,cy))
            {
                color[cx][cy]=1;
            }
            dx=i*6,dy=j*a;
            cx=dx+x,cy=dy+y;
            if(valid(cx,cy))
            {
                color[cx][cy]=1;
            }
            dx=i*a,dy=j*1;
            cx=dx+x,cy=dy+y;
            if(valid(cx,cy))
            {
                color[cx][cy]=1;
            }
            dx=i*a,dy=j*6;
            cx=dx+x,cy=dy+y;
            if(valid(cx,cy))
            {
                color[cx][cy]=1;
            }
        }
    }
}

void Grid::mousePressEvent(QMouseEvent *e)
{
    emit _event(true);
    double cellWidth = (double)width()/grid_size;
    double cellHeight = (double)height()/grid_size;
    int k = floor(e->y()/cellHeight)+1;
    int j = floor(e->x()/cellWidth)+1;
    if(valid(k,j))
    {
        switch(key_type)
        {
            case 0:color[k][j] = !color[k][j];break;
            case 1:draw_rect(k,j,randint(1,10),randint(1,10));break;
            case 2:draw_plane(k,j);break;
            case 3:draw_star(k,j);break;
            case 4:draw_ship(k,j);break;
            default:assert(0);
        }
    }
    update();
}

void Grid::mouseMoveEvent(QMouseEvent *e)
{
    double cellWidth = (double)width()/grid_size;
    double cellHeight = (double)height()/grid_size;
    int k = floor(e->y()/cellHeight)+1;
    int j = floor(e->x()/cellWidth)+1;
    if(valid(k,j)&&!color[k][j])
    {
        color[k][j]=1;
        update();
    }
}

int Grid::gen(int x, int y)
{
    int cnt=0;
    for(int i=-1;i<=1;++i)for(int j=-1;j<=1;++j)
    {
        if((i||j)&&valid(x+i,y+j))cnt+=color[x+i][y+j];
    }
    auto&is=color[x][y];
    if(!is)return cnt==3;
    else return cnt>=2&&cnt<=3;
}

void Grid::next_state()
{
    auto&res=nxc;
    for(int i=0;i<grid_size;++i)for(int j=0;j<grid_size;++j)if(valid(i,j))res[i][j]=gen(i,j);
}

bool Grid::check_dump_state()
{
    next_state();
    for(int i=0;i<grid_size;++i)for(int j=0;j<grid_size;++j)if(valid(i,j)&&nxc[i][j]!=color[i][j])return false;
    return true;
}

void Grid::next_step()
{
    if(check_dump_state())
    {
        mytimer->stop();
        return;
    }
    for(int i=0;i<grid_size;++i)for(int j=0;j<grid_size;++j)if(valid(i,j))color[i][j]=nxc[i][j];
    update();
}

void Grid::paintGrid(QPainter &p)
{
    QRect borders(0, 0, width()-1, height()-1);
    QColor gridColor = origin_color;
    gridColor.setAlpha(20);
    p.setPen(gridColor);
    double cellWidth = (double)width()/grid_size;
    for(double k = cellWidth; k <= width(); k += cellWidth)
        p.drawLine(k, 0, k, height());
    double cellHeight = (double)height()/grid_size;
    for(double k = cellHeight; k <= height(); k += cellHeight)
        p.drawLine(0, k, width(), k);
    p.drawRect(borders);
}

void Grid::paintUniverse(QPainter &p)
{
    double cellWidth = (double)width()/grid_size;
    double cellHeight = (double)height()/grid_size;
    for(int k=1; k <= grid_size; k++) {
        for(int j=1; j <= grid_size; j++) {
            if(valid(k,j)&&color[k][j] == 1) {
                qreal left = (qreal)(cellWidth*j-cellWidth);
                qreal top  = (qreal)(cellHeight*k-cellHeight);
                QRectF r(left, top, (qreal)cellWidth, (qreal)cellHeight);
                p.fillRect(r, QBrush(origin_color));
            }
        }
    }
}
